<?php 
/**
 * 权限项页面
 * @author hfc
 * @since 2015-6-29
 */
namespace apps\admin\controllers;
if( !defined( 'APP_ROOT' ) ){ echo 'Direct Access Deny!'; return; }

use enums\PriEnums;


use apps\admin\ext\AdminBaseController;
use apps\admin\models\PriPris;
use enums\DBEnums;;
use Phalcon\Paginator\Adapter\QueryBuilder;
use Phalcon\Validation;
use Phalcon\Validation\Validator\PresenceOf;
use apps\admin\models\PriAcl;
use apps\admin\models\PriPrisAcl;
use apps\admin\biz\PrivilegeBiz;
use apps\admin\enums\DisplayEnums;

class PrivilegeController extends AdminBaseController 
{
    public function initialize() 
    {
        parent::initialize();
    	//$this->checkPlatform();
    }
    
    /**
    * @author( author='hfc' )
    * @date( date = '2016-1-18' )
    * @comment( comment = '权限项首页' )	
    * @method( method = 'indexAction' )
    * @op( op = 'r' )		
   */
    public function indexAction()
    {
        $iPage = $this->request->getQuery( 'page', 'int' );
        $currentPage = $iPage ?: 1;
        
        $builder = $this->modelsManager->createBuilder()
                    ->columns( 'id,name,display,src,apid, pid, sort' )
                    ->from( 'apps\admin\models\PriPris' )
                    ->where( 'pid=1 and delsign=' . DBEnums::DELSIGN_NO )
                    ->orderBy( 'sort,id');
        
        $paginator = new QueryBuilder( array( 
            'builder'  => $builder,
            'limit' => DisplayEnums::PER_PAGE_LIST_NUM,
            'page'  => $currentPage
        ));
        
        $page = $paginator->getPaginate();
        $this->view->setVars( array('page' => $page ));
    }
    
    /**
    * @author( author='hfc' )
    * @date( date = '2016-1-19' )
    * @comment( comment = '权限项删除' )	
    * @method( method = 'deleteAction' )
    * @op( op = 'd' )		
   */
    public function deleteAction()
    {
    	if( ! $this->csrfCheck() ){ return false; }
    	
        $id = $this->request->getPost( 'id', 'int' );
        if( ! $id )
        {
        	return $this->error( '非法id' );
        	return;	
        }
        
        //首先判断是否有子权限
    	$objPri = PriPris::findFirst( array( 'pid=?0 and delsign=' . DBEnums::DELSIGN_NO, 'bind' => array( $id ) ));
        if( $objPri )
        {
        	return $this->error( '有子权限不可直接删除' );
        	return;
        }

        //软删除
        $objPri = PriPris::findFirst( array( 'id=?0 and delsign=' . DBEnums::DELSIGN_NO, 'bind' => array( $id )) );
		if( $objPri && $objPri->delete() )        	
		{
			return $this->success( '删除成功' );
		}
			
        return $this->error( '删除失败' );
    }
    
    /**
    * @author( author='hfc' )
    * @date( date = '2016-1-19' )
    * @comment( comment = '获得权限' )	
    * @method( method = 'getSubAction' )
    * @op( op = 'r' )		
   */
    public function getSubAction()
    {
        $id = $this->request->getQuery( 'id', 'int' );
        if( $id )
        {
            $arr = PriPris::find( [  'pid=?0 and delsign=' . DBEnums::DELSIGN_NO, 
                    'bind' => [ $id ], 'columns' => 'id,name,display,sort' , 'order' => 'sort asc' ] )->toArray();
            if( $arr )
            {
                return $this->success( '成功', [ 'data' => $arr ] );
            }
        }
        return $this->error( '无子界面元素' );
    }
    
    /**
    * @author( author='hfc' )
    * @date( date = '2016-1-19' )
    * @comment( comment = '编辑权限' )	
    * @method( method = 'editAction' )
    * @op( op = 'r' )		
   */
    public function editAction()
    {
        $id = $this->dispatcher->getParam( 'id', 'int' );
        if( ! $id )
        {
        	return false;
        }
        
        $objPri = PriPris::findFirst( [  'id=?0', 'bind' => [ $id ], 'columns' => 'id,pid,name,display,sort,loadmode,icon' ] );
        if( ! $objPri )
        {
        	return false;
        }
		        
        if(  $objPri->pid  ) //获得上级
        {
            $objParent = PriPris::findFirst( [  'id=' . $objPri->pid, 'columns' => 'id,name' ] );
            if( $objParent )
            {
            	$arrParam[ 'pid' ] = $objPri->pid;
            	$arrParam[ 'pname' ] = $objParent->name; 
            }        
        }
        
        //获得acl
		$objPriAcl = new PrivilegeBiz();                        
        $arrParam[ 'priAcl' ] = $objPriAcl->getPriAcl( $id );
        
       	$arrParam[ 'pri' ] = $objPri->toArray();
       	$view = $this->view->getPartial( 'privilege/add', $arrParam );
       	return $this->success( '获取页面成功', array( 'view' => $view ) );
    }
    
    /**
    * @author( author='hfc' )
    * @date( date = '2016-1-19' )
    * @comment( comment = '添加权限' )	
    * @method( method = 'addAction' )
    * @op( op = 'r' )		
   */
    public function addAction()
    {
    	$arr = array( 'add' => true );
        $pid = $this->dispatcher->getParam( 'pid', 'int' );
        if( $pid )
        {
            $objPri = PriPris::findFirst( [  'id=?0', 'bind' => [ $pid ], 'columns' => 'id,name' ] );
            if( $objPri )
            {
            	$arr[ 'pid' ] = $pid;
            	$arr[ 'pname' ] = $objPri->name;
            }
        }
		$view = $this->view->getPartial( 'privilege/add', $arr );
		$this->success( '获取页面成功', array( 'view' => $view ));
		return false;
    }
    
     /**
    * @author( author='hfc' )
    * @date( date = '2016-1-19' )
    * @comment( comment = '保存权限' )	
    * @method( method = 'saveAction' )
    * @op( op = 'u' )		
   */
    public function saveAction()
    {
        if( ! $this->csrfCheck() ){ return false; }
        $arrAcl = $this->request->getPost( 'acl' );

        if( !count( $this->request->getPost()))
        {
            return $this->error( '无数据！' );
        }
        
        $id = $this->request->getPost( 'id', 'int' );
        $pid = $this->request->getPost( 'pid', 'int' );
        $data[ 'name' ] = $this->request->getPost( 'name', 'string' );
        $data['icon'] = $this->request->getPost( 'icon', 'string' );
        $data[ 'display' ] = $this->request->getPost( 'display', 'int');
        $data[ 'loadmode' ] = $this->request->getPost( 'loadmode', 'int' );
        $sort = $this->request->getPost( 'sort', 'int' );
        $defaultId = $this->request->getPost( 'defaultId', 'int' );
        
        $data[ 'sort' ] = $sort ? $sort : 0;
        $this->validation( $data );
        $this->db->begin();
        if( $id ) //编辑
        {
        	$objPri = PriPris::findFirst( array( 'id=?0 and delsign=' . DBEnums::DELSIGN_NO , 'bind' => [ $id ] ) );
        	if(  ! $objPri || ! $objPri->update( $data )  )
        	{
        		$this->db->rollback();
        		return $this->error( '更新失败' );
        	}
        }
        else  //新建
        {
            $data[ 'pid' ] = $pid ?: 1;
            $data[ 'addtime' ] = $data[ 'uptime' ] = date( 'Y-m-d H:i:s' );
            $data[ 'delsign' ] = DBEnums::DELSIGN_NO;
            $objPri = new PriPris();
            if( ! $objPri->save( $data ))
            {
				$this->db->rollback();
				return $this->error( '新建失败' );            	
            }
         	$id = $objPri->id;
        }

        //保存acl
        $objPriAcl = new PriPrisAcl();
        $status = $objPriAcl->savePriAcl( $id, $arrAcl, $defaultId );
        if( $status )
        {
        	$this->db->rollback();
        	return $this->error( '保存acl失败');
        }
        
        $this->db->commit();
        return $this->success( '保存成功' );
    }
    
    
    /**
    * @author( author='hfc' )
    * @date( date = '2016-1-20' )
    * @comment( comment = '验证' )	
    * @method( method = 'validation' )
    * @op( op = '' )		
   */
    private function validation( $data )
    {
        $validation = new Validation();
        $validation->add( 'name', new PresenceOf( [ 'message' => '菜单名必填']) );
        $messages = $validation->validate( $data );
        
        if( count( $messages ))
        {
            $strMsg = '';
            foreach( $messages as $msg )
            {
                $strMsg .= $msg->getMessage() . '<br>'; 
            }
            return $this->error( $strMsg );
        }
    }
      
    /**
     * @author( author='hfc' )
     * @date( date = '2016-10-31' )
     * @comment( comment = '获得所有的acl, 树形结构' )
     * @method( method = 'getAclAction' )
     * @op( op = '' )
     */
    public function getAclAction()
    {
    	$objPris = PriAcl::find( array( 'delsign=' . DBEnums::DELSIGN_NO, 
	    			'columns' => 'id, pid, name  as text,delsign,type ', 'order' => 'id' ) );
    	
    	$arr = array();
    	$arrPris = $objPris->toArray();
    	foreach( $arrPris as $item ) //以id 为键
    	{
    		$arr[ $item[ 'id' ] ] = $item;
    	}
    	
    	$arrTree = array();
    	foreach( $arr as $key => $item )
    	{  
    		if( ! empty( $arr[ $item[ 'pid']]))
    		{
				$arr[ $item[ 'pid'] ][ 'nodes' ][ $item[ 'id' ]] = &$arr[ $key ]; //是子   		
    		}
    		else if( $item[ 'delsign' ] == DBEnums::DELSIGN_NO )
    		{
	    		$arrTree[] = &$arr[ $key ]; //没有父
    		}
    		unset( $arr[ $key ][ 'delsign' ], $arr[ $key ][ 'pid'] );
    	}
    	return $this->success( '获取acl成功', array( 'data' => json_encode( $arrTree )));
    }
    
    
    /**
     * @author( author='hfc' )
     * @date( date = '2016-10-31' )
     * @comment( comment = '更新模块下所有的类到数据库' )
     * @method( method = 'updateAclAction' )
     * @op( op = '' )
     */
    public function updateAclAction()
    {
    	$module = $this->dispatcher->getParam( 'module', 'string' );
  		if( ! $module )  	
  		{
  			return $this->error( '非法module' );
  		}
  		$objModule = PriAcl::findFirst( 'name="' . $module . '" and pid = 0 and type=' . PriEnums::PRI_MODULE );
    	if( ! $objModule ) //还没有module 新建一个		
    	{
    		$objModule = new PriAcl();
    		$status = $objModule->save( array( 'name' => $module, 'pid' => 0, 'type' => PriEnums::PRI_MODULE ));
    		if( ! $status )
    		{
    			return $this->error( '新建模块' . $module . '失败' );
    		}
    	}
    	
    	//读取文件中的controller
    	$path = APP_ROOT . 'apps/' . $module .'/controllers' ;
    	if( ! file_exists( $path ))
    	{
    		return $this->error( $path . ' 目录不存在' );
    	}
    	$fp = scandir( $path );
    	$arrClsAct[$module] = array();
    	foreach( $fp as $item )
    	{
    		if( $item == '.' || $item == '..')
    		{
    			continue;
    		}
    
    		$iPos = strpos( $item, 'Controller.php' );
    		if( $iPos == false )
    		{
    			continue;
    		}
    		$strController = substr( $item, 0, $iPos );
    		$className = '\apps\\' . $module . '\controllers\\' . $strController . 'Controller';
    		$arrMethod = get_class_methods( $className );
    		if( $arrMethod ) //获取到方法
    		{
    			$condition = 'name="' . $strController . '" and pid=' . $objModule->id . ' and type=' . PriEnums::PRI_CONTROLLER;
	    		$objController = PriAcl::findFirst( $condition  ) ;
	    		if( $objController ) //找到控制器
	    		{
	    			$cid = $objController->id;
	    		}
	    		else //新建一个控制器
	    		{
	    			$objController = new PriAcl();
	    			$data = array( 'name' => $strController, 'pid' => $objModule->id, 'type' => PriEnums::PRI_CONTROLLER );
	    			$status = $objController->save( $data );
	    			if( ! $status )
	    			{
	    				return $this->error( '新建控制器' . $strController . '失败' );
	    			}
	    			$cid = $objController->id;
	    		}
	    		
    			foreach( $arrMethod as $item )
    			{
    				$ipos = strpos( $item, 'Action' );
    				if( $ipos !== false )
    				{
    					$strAction = substr( $item, 0, $ipos );
    				    $arrClsAct[$module][ $strController ][$strAction] = 1;
    					$obj = PriAcl::findFirst( 'name="' . $strAction . '" and type=' .PriEnums::PRI_ACTION  . ' and pid=' . $cid ) ;
    					if( ! $obj )//新建操作
    					{
    						$obj = new PriAcl();
    						$status = $obj->save( array( 'name' => $strAction, 'pid' => $cid, 'type' => PriEnums::PRI_ACTION ));
    						if( ! $status )
    						{
    							return $this->error( '新建操作' . $strAction . '失败');
    						}
    					}
    				}
    			}
    		}
    	}
    	
    	
    	$this->dataCache->delete( PriEnums::PREFIX_ACL_ADMIN );
    	
    	$this->checkValidate( $arrClsAct );
    	return $this->success( '更新acl数据库成功' );
    }
    
    private function checkModule( $strModuleName )
    {
        if( file_exists( APP_ROOT . 'apps/' . $strModuleName ))
        {
            return true;
        }
        
        return false;
    }
    
    private function getInIDs( $arrToDel, $strType = null )
    {
        
        if( empty( $arrToDel[ $strType ] ))
        {
            return null;
        }
        
        $iCnt = count( $arrToDel[ $strType ] );
        if( $iCnt > 0 )
        {//清理无用的action
            $iIndex = 0;
            $strIDs = '(';
            
            foreach( $arrToDel[ $strType ] as $id )
            {
                if( ++$iIndex != $iCnt )
                {
                    $strIDs .= $id . ',';
                }
                else
                {
                    $strIDs .= $id . ')';
                }
            }
            
            return $strIDs;
        }
        
        return null;
    }
    
    private function cleanUnusedPris( $arrToDel )
    {
        if( $arrToDel == null || count( $arrToDel ) == 0 )
        {
            return;
        }
        
        if( isset( $arrToDel['controllers'] ))
        {
            //controllers
            //清除无用的controllers
            $strCtrlIDs = $this->getInIDs( $arrToDel, 'controllers' );
            //待删除的元素（用于中间表格）
            $query = PriAcl::find( array( 'conditions' => 'pid in ' . $strCtrlIDs, 'columns' => 'id' ));
            $arrIDs = $query->toArray();
            
            $arrStrToDel = array();
            
            $iCnt = count( $arrIDs );
            if( $iCnt > 0 )
            {
                $strIDs = '(';
                for( $i = 0; $i < $iCnt; ++$i )
                {
                    if( $i != $iCnt - 1 )
                    {
                        $strIDs .= $arrIDs[$i]['id'] . ',';
                    }
                    else
                    {
                        $strIDs .= $arrIDs[$i]['id'] . ')';
                    }
                }
                $arrStrToDel[] = 'delete from \apps\admin\models\PriPrisAcl where acl_id in ' . $strIDs;
            }
            
            
            $arrStrToDel[] = 'delete from \apps\admin\models\PriAcl where pid in ' . $strCtrlIDs;
            $arrStrToDel[] = 'delete from \apps\admin\models\PriAcl where id in ' . $strCtrlIDs;
        }
        

        if( isset( $arrToDel['actions'] ))
        {
            //actions
            $strActionIDs = $this->getInIDs( $arrToDel, 'actions' );
            //清除无用的actions
            $arrStrToDel[] = 'delete from \apps\admin\models\PriAcl where id in ' . $strActionIDs;
            //清除中间表格
            $arrStrToDel[] = 'delete from \apps\admin\models\PriPrisAcl where acl_id in ' . $strActionIDs;
        }
        
        try 
        {
            $this->db->begin();
            
            foreach( $arrStrToDel as $phql )
            {
                $this->modelsManager->executeQuery($phql);
            }
            
            $this->db->commit();
        }
        catch ( \Exception $e )
        {
            $this->db->rollback();
            $this->errLog->error( $this->appVer['appVersion'] . '|' . $this->router->getModuleName() . '|' . __METHOD__ . '|权限配置:' . $e->getMessage() );
        }
        
    }
    
    private function checkValidate( $arrClsAct )
    {
        assert( $arrClsAct );
        
        $objAcls = PriAcl::find();
        
        assert( $objAcls );
        
        $arrPids = array();
        $arrAcls = $objAcls->toArray();
        
        foreach( $arrAcls as $item )
        {
            $arrPids[$item['pid']][$item['id']] = $item;
        }
        
        $arrToDel = array();
        $topItems = $arrPids[0];
        
        foreach( $topItems as $mKey => $mVal )
        {
            //查看module是否存在
            if( !$this->checkModule( $mVal['name'] ))
            {//删除所有以module为pid的子孙
                $arrToDel['modules'][] = $mKey;
                continue;
            }
            
            foreach( $arrPids[$mKey] as $cKey => $cVal )
            {
                if( !file_exists( APP_ROOT . 'apps/' . $mVal['name'] . '/controllers/' . $cVal['name'] . 'Controller.php' ) )
                {//遍历查看module中的所有控制器
//                     var_dump(APP_ROOT . 'apps/' . $mVal['name'] . '/' . $cVal['name'] . 'Controller.php');
                    
                    $arrToDel['controllers'][] = $cKey;
                    continue;
                }
                
                if( isset( $arrPids[$cKey] ))
                foreach( $arrPids[$cKey] as $aKey => $aVal )
                {
                    if( empty( $arrClsAct[$mVal['name']][ $cVal['name'] ][$aVal['name']] ))
                    {
                        $arrToDel['actions'][] = $aKey;
                        continue;
                    }
                }
                
            }
            //遍为控制器中的action
        }
        
        $this->cleanUnusedPris($arrToDel);
      
    }
}

